package com.rency.common.manager.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rency.common.manager.ModelEnum;
import com.rency.common.manager.ServletUtils;

public abstract class ResourceServlet extends HttpServlet{

	/**
	 * 
	 */
	private static final long serialVersionUID = 4749836746388425195L;
	
	protected static final Logger logger = LoggerFactory.getLogger(ResourceServlet.class);
	
	protected static final String PARAM_SESSION_KEY = "_COMMON_SESSION_KEY_";
	protected static final String PARAM_ROOT_PATH   = "common/";
	
	protected static final String PARAM_NAME_USERNAME = "loginUsername";
	protected static final String PARAM_NAME_PASSWORD = "loginPassword";
	protected static final String PARAM_NAME_MODEL = "modelName";
	protected static final String PARAM_NAME_ALLOW_REMOTE_ADDR   = "allowRemoteAddress";
	
	protected final String     resourcePath;
	
	protected String           username                   = null;
    protected String           password                    = null;
    protected List<String> modelList                    = new ArrayList<String>();
    protected List<String> allowRemoteAddrList  = new ArrayList<String>();
	
	public ResourceServlet(String resourcePath){
        this.resourcePath = resourcePath;
    }

	@Override
	public void init() throws ServletException {
		initAuthEnv();
	}
	
	private void initAuthEnv() {
		String paramUserName = getInitParameter(PARAM_NAME_USERNAME).trim();
		if(StringUtils.isNotBlank(paramUserName)){
			this.username = paramUserName;
		}
		
		String paramPassword = getInitParameter(PARAM_NAME_PASSWORD).trim();
		if(StringUtils.isNotBlank(paramPassword)){
			this.password = paramPassword;
		}
		
		String paramModel = getInitParameter(PARAM_NAME_MODEL).trim();
		try{
			if(StringUtils.isNotBlank(paramModel)){
				String[] items = paramModel.split(",");
				for(String item : items){
					if(StringUtils.isBlank(item)){
						continue;
					}
					if(ModelEnum.get(item) == null){
						throw new IllegalArgumentException("initParameter config error, model [" + item + "] not exists.");
					}
					modelList.add(item);
				}
			}
		}catch(Exception e){
			logger.error("initParameter config error, model : [{}]",paramModel,e);
		}
		
		String paramAllowRemoteAddr = getInitParameter(PARAM_NAME_ALLOW_REMOTE_ADDR).trim();
		try{
			if(StringUtils.isNotBlank(paramAllowRemoteAddr)){
				String[] items = paramAllowRemoteAddr.split(",");
				for(String item : items){
					if(StringUtils.isNotBlank(item)){
						allowRemoteAddrList.add(item);
					}
				}
			}
		}catch(Exception e){
			logger.error("initParameter config error, allowRemoteAddr : [{}]",paramModel,e);
		}
	}

	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String contextPath = request.getContextPath();
        String servletPath = request.getServletPath();
        String requestURI = request.getRequestURI();
        response.setCharacterEncoding("utf-8");

        if (contextPath == null) { // root context
            contextPath = "";
        }
        String uri = contextPath + servletPath;
        String path = requestURI.substring(contextPath.length() + servletPath.length());
        
        if (!isPermittedRequest(request,path)) {
            path = "/nopermit.html";
            returnResourceFile(path, uri, response);
            return;
        }
        
        if ("/submitLogin".equals(path)) {
            String usernameParam = request.getParameter(PARAM_NAME_USERNAME);
            String passwordParam = request.getParameter(PARAM_NAME_PASSWORD);
            if (username.equals(usernameParam) && password.equals(passwordParam)) {
                request.getSession().setAttribute(PARAM_SESSION_KEY, username);
                response.getWriter().print("success");
            } else {
                response.getWriter().print("error");
            }
            return;
        }
        
        if(path.contains("/logout.json")) {
        	if(request.getSession().getAttribute(PARAM_SESSION_KEY) != null){
        		request.getSession().removeAttribute(PARAM_SESSION_KEY);
        	}
        	response.getWriter().print("success");
        	return;
        }
        
        if(isRequireAuth() && !ContainsUser(request) && !("/login.html".equals(path) || path.startsWith("/css") || path.startsWith("/js") || path.startsWith("/img"))){
        	if (contextPath.equals("") || contextPath.equals("/")) {
                response.sendRedirect("/"+PARAM_ROOT_PATH+"login.html");
            } else {
                if ("".equals(path)) {
                    response.sendRedirect(PARAM_ROOT_PATH+"login.html");
                } else {
                    response.sendRedirect("login.html");
                }
            }
            return;
        }
        
        if ("".equals(path)) {
            if (contextPath.equals("") || contextPath.equals("/")) {
                response.sendRedirect("/"+PARAM_ROOT_PATH+"index.html");
            } else {
                response.sendRedirect(PARAM_ROOT_PATH+"index.html");
            }
            return;
        }
        
        if ("/".equals(path)) {
            response.sendRedirect("index.html");
            return;
        }
        
        if(path.contains(".json")) {
            String fullUrl = path;
            if (request.getQueryString() != null && request.getQueryString().length() > 0) {
                fullUrl += "?" + request.getQueryString();
            }
            response.getWriter().print(process(fullUrl));
            return;
        }

        // find file in resources path
        returnResourceFile(path, uri, response);
	}
	
	private boolean isPermittedRequest(HttpServletRequest request,String path){
		if("/index.html".equals(path) || "/login.html".equals(path) || "/header.html".equals(path) || path.indexOf(".js") != -1 || path.indexOf(".json") != -1 || path.indexOf(".css") != -1 || "/submitLogin".equals(path)){
			return true;
		}
		if(path.indexOf(".html") != -1){
			path = path.substring(0,path.indexOf(".html"));
		}
		String remoteAddress = request.getRemoteAddr();
        for(String addr : allowRemoteAddrList){
        	if(addr.equals(remoteAddress)){
        		return true;
        	}
        }
        
        if(path.startsWith("/")){
        	path = path.substring(1, path.length());
        }
        if(StringUtils.isNotBlank(path) && !modelList.contains(path)){
        	return false;
        }
        return true;
	}
	
	protected void returnResourceFile(String fileName, String uri, HttpServletResponse response) throws ServletException,IOException {
		String filePath = getFilePath(fileName);
		if (fileName.endsWith(".jpg")) {
			byte[] bytes = ServletUtils.readByteArrayFromResource(filePath);
			if (bytes != null) {
				response.getOutputStream().write(bytes);
			}
			return;
		}

		String text = ServletUtils.readFromResource(filePath);
		if (text == null) {
			response.sendRedirect(uri + "/index.html");
			return;
		}
		if (fileName.endsWith(".css")) {
			response.setContentType("text/css;charset=utf-8");
		} else if (fileName.endsWith(".js")) {
			response.setContentType("text/javascript;charset=utf-8");
		}
		response.getWriter().write(text);
	}
	
	protected String getFilePath(String fileName) {
        return resourcePath + fileName;
    }
	
	public boolean ContainsUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        return session != null && session.getAttribute(PARAM_SESSION_KEY) != null;
    }
	
	public boolean isRequireAuth() {
        return StringUtils.isNotBlank(this.username);
    }
	
	protected abstract String process(String url);
}
